﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Practices.Unity;
using Wyn.PMS.Common.SecurityControl;
using Wyn.PMS.Common.Utils;
using Wyn.PMS.Entity.Entities;
using Wyn.PMS.Entity.IDataAccess;

namespace Wyn.PMS.Common.NightAudit
{
    /// <summary>
    /// 房价码表的缓存读取与更新
    /// <para>--------------------------</para>
    /// <para>@Author: cnalexi@gmail.com</para> 
    /// <para>@Create: 2012/3/30</para>  
    /// </summary>
    public class PriceCache
    {
        #region ☆ -------- 初始化做预备 -------- ☆

        /// <summary>
        /// 价格缓存 Key:RoomPriceID
        /// </summary>
        private IDictionary<string, RoomPrice> _priceCache = new Dictionary<string, RoomPrice>();

        /// <summary>
        /// 折扣代码 Key:RateCodeID
        /// </summary>
        private IDictionary<string, RateCode> _rateCache = new Dictionary<string, RateCode>();
        
        private readonly DateTime InitTimeStamp = DateTime.Parse("1984/11/09 18:00:00 PM");

        private DateTime _priceVersionTimeStamp = DateTime.Parse("1984/11/09 18:00:00 PM");

        private DateTime _rateVersionTimeStamp = DateTime.Parse("1984/11/09 18:00:00 PM");

        /// <summary>
        /// 过期时间
        /// </summary>
        private DateTime _expirationTime = DateTime.Parse("2114/11/09 18:00:00 PM");

        /// <summary>
        /// 房价最长过期时间为8小时(480分钟) !important
        /// </summary>
        private const int LIFETIME = 480;

        private static readonly IUnityContainer container = UnityContainerService.GetContainer();
        
        private static readonly ReaderWriterLockSlim barrier = new ReaderWriterLockSlim();

        /// <summary>
        /// 刷新线程
        /// </summary>
        private Thread _refreshTread;

        private static volatile PriceCache _instance;

        private static readonly object _lock = new object();
            
        /// <summary>
        /// Key:RoomPriceID
        /// </summary>
        private ReadonlyDictionary<string, RoomPrice> _readonlyPice;

        /// <summary>
        /// Key:RateCodeID
        /// </summary>
        private ReadonlyDictionary<string, RateCode> _readonlyRateCode;
        
        private PriceCache(){}

        public static PriceCache GetInstance()
        {
            if(_instance==null)
            {
                lock(_lock)
                {
                    if(_instance==null)
                    {
                        var pc = new PriceCache();

                        pc.InitCache();

                        _instance = pc;

                        Thread.MemoryBarrier();
                    }
                }
            }

            return _instance;
        }

        public IDictionary<string, RoomPrice> ReadonlyPrice
        {
            get
            {
                if (_readonlyPice == null || _readonlyPice.Count<=0)
                {
                    InitCache();
                }
                return _readonlyPice;
            }
            
        }

        public IDictionary<string, RateCode> ReadonlyRate
        {
            get
            {
                if (_readonlyRateCode == null||_readonlyRateCode.Count<=0)
                {
                    InitCache();
                }
                return _readonlyRateCode;
            }

        }

        #endregion

        #region ☆ -------- 刷新处理逻辑 -------- ☆

        /// <summary>
        /// 创建和初始化房价缓存
        /// </summary>
        private void InitCache()
        {

            var roomPriceRepository = container.Resolve<IRoomPriceRepository>();

            try
            {
                IList<string> hotelIds = BizDay.GetAll().Select(h => h.HotelID).ToList();
                
                foreach (var hotelId in hotelIds)
                {

                    object[] priceRates= null;

                    var transactionOptions = new System.Transactions.TransactionOptions();

                    transactionOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted;

                    using (var scope = new System.Transactions.TransactionScope(System.Transactions.TransactionScopeOption.Required, transactionOptions))
                    {
                        priceRates = roomPriceRepository.GetPriceRate(hotelId, InitTimeStamp,InitTimeStamp);

                        scope.Complete();
                    }

                    var prices = (IList<RoomPrice>) priceRates[0];

                    if (prices.Count>0)FormatPriceCache(prices);
                        
                    var rates = (IList<RateCode>) priceRates[1];

                    if (rates.Count>0)FormatRateCodeCache(rates);
                }

            }catch(Exception error)
            {
                OperateLog.ErrorLog(DateTime.Now + "房价初始化失败" + error);
            }

            //_expirationTime = DateTime.Now.AddMinutes(LIFETIME);//每3小时候过期一次
            
            ////释放刷新线程
            //if (_refreshTread != null)
            //{
            //    try
            //    {
            //        _refreshTread.Abort();

            //        Thread.Sleep(100);
            //    }
            //    catch (Exception ex)
            //    {
            //        OperateLog.ErrorLog(DateTime.Now + "房价缓存初始化失败" + ex.Message);
            //    }

            //    if (_refreshTread.IsAlive)
            //    {
            //        throw new ThreadStateException("param't Restart refresh cache Thread");
            //    }
            //}
        }

        /// <summary>
        /// 更新房价缓存[修改房价时调用]
        /// </summary>
        public void UpdatePriceCache()
        {
            var _roomPriceRepository = container.Resolve<IRoomPriceRepository>();

            Expression<Func<RoomPrice, bool>> exp = p => p.ModifiedTime > _priceVersionTimeStamp;

            try
            {

                IList<RoomPrice> prices = null;

                var transactionOptions = new System.Transactions.TransactionOptions();

                transactionOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted;

                using (var scope = new System.Transactions.TransactionScope(System.Transactions.TransactionScopeOption.Required, transactionOptions))
                {
                    prices = _roomPriceRepository.BatchFind(GlobalConst.ROOT_HOTEL_ID, exp);

                    scope.Complete();
                }

                if(prices!=null&&prices.Count>0) FormatPriceCache(prices);
            
            }catch(Exception error)
            {
                OperateLog.ErrorLog(DateTime.Now + "基础房价刷爆啦" + error);
            }
        }

        /// <summary>
        /// 更新折扣缓存[修改折扣时调用]
        /// </summary>
        public void UpdateRateCache()
        {
            var _rateCodeRepository = container.Resolve<IRateCodeRepository>();

            Expression<Func<RateCode, bool>> exp = r => r.ModifiedTime > _rateVersionTimeStamp;
            
            try
            {
                IList<RateCode> rates = null;

                var transactionOptions = new System.Transactions.TransactionOptions();

                transactionOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted;

                using (var scope = new System.Transactions.TransactionScope(System.Transactions.TransactionScopeOption.Required, transactionOptions))
                {
                    rates = _rateCodeRepository.BatchFind(GlobalConst.ROOT_HOTEL_ID, exp);

                    scope.Complete();
                }

                if (rates != null && rates.Count > 0) FormatRateCodeCache(rates);
            
            
            }catch(Exception error)
            {
                OperateLog.ErrorLog(DateTime.Now + "房价码刷爆啦" + error);
            }
        }

        /// <summary>
        /// 格式化房价缓存
        /// </summary>
        /// <param name="prices"></param>
        private void FormatPriceCache(IList<RoomPrice> prices)
        {
            _priceVersionTimeStamp = prices.Max(p => p.ModifiedTime);
            
            foreach (var price in prices)
            {
                string key = price.RoomPriceID.ToString(CultureInfo.InvariantCulture);
                
                if (_priceCache.ContainsKey(key))
                {
                    _priceCache.Remove(key);
                }

                if (price.IsValid)
                {
                    _priceCache.Add(key, price);
                }
            }
            
            var readonlyPrices = new ReadonlyDictionary<string, RoomPrice>(_priceCache);

            _readonlyPice = readonlyPrices;
        }

        /// <summary>
        /// 格式化折扣缓存
        /// </summary>
        /// <param name="rates"></param>
        private void FormatRateCodeCache(IList<RateCode> rates)
        {
            _rateVersionTimeStamp = rates.Max(r => r.ModifiedTime);
            
            foreach(var rate in rates)
            {
                string key = rate.RateCodeID.ToString(CultureInfo.InvariantCulture);
                
                if (_rateCache.ContainsKey(key))
                {
                    _rateCache.Remove(key);
                }

                if (rate.IsValid)
                {
                    _rateCache.Add(key, rate);
                }
            }
            
            var readonlyRates = new ReadonlyDictionary<string, RateCode>(_rateCache);

            _readonlyRateCode = readonlyRates;
        }

        /// <summary>
        /// 重刷新房价折扣缓存
        /// </summary>
        private void RefreshCache()
        {
            if (DateTime.Now >= _expirationTime)
            {
                if (_refreshTread.ThreadState != ThreadState.Running)
                {
                    _refreshTread = new Thread(InitCache)
                                        {
                                            Name = "PriceLoopThread",
                                            Priority = ThreadPriority.AboveNormal,
                                            IsBackground = true
                                        };
                    
                    _refreshTread.Start();

                }
            }
        }

        #endregion

        #region ☆ -------- 外部访问方法 -------- ☆

        /// <summary>
        /// 获取房价缓存
        /// </summary>
        /// <returns></returns>
        public IList<RoomPrice> GetRoomPriceCache(PriceParameter param)
        {
            //过期规则 每3小时重建一次价格缓存
            //RefreshCache();

            return ReadonlyPrice.Values.Where(p =>
                                              (p.HotelID == param.HotelId || p.HotelID == GlobalConst.ROOT_HOTEL_ID)
                                              && p.RoomTypeID == param.RoomTypeId
                                              && p.StartDate <= param.EndDate
                                              && p.EndDate >= param.StartDate
                                              && p.IsValid).ToList();
        }

        public decimal GetRoomPrice(string hotelId,string roomTypeId,DateTime bizDay)
        {
            var price = ReadonlyPrice.Values.Where(p =>
                                                   (p.HotelID == hotelId || p.HotelID == GlobalConst.ROOT_HOTEL_ID)
                                                   && p.RoomTypeID == roomTypeId
                                                   && p.StartDate <= bizDay
                                                   && p.EndDate >= bizDay
                                                   && p.IsValid).OrderByDescending(p => p.Weight).FirstOrDefault();

            if (price == null) return 0;
            
            string dayOfWeek = ((int)bizDay.DayOfWeek).ToString(CultureInfo.InvariantCulture);

            decimal normalPrice = (!string.IsNullOrEmpty(price.Week) && price.Week.IndexOf(dayOfWeek) >= 0)
                                      ? price.NormalPrice + price.FluctuatingPrice
                                      : price.NormalPrice;

            return normalPrice;
        }

        /// <summary>
        /// 获取折扣码缓存
        /// </summary>
        /// <returns></returns>
        public IList<RateCode> GetRateCodeCache(PriceParameter param,DateTime bizDay)
        {
            //RefreshCache();

            var sourceRates = ReadonlyRate.Values.Where(r =>
                                                       (r.HotelID == param.HotelId || r.HotelID==GlobalConst.ROOT_HOTEL_ID)
                                                       && r.RoomTypeID == param.RoomTypeId
                                                       && r.StartDate <= bizDay
                                                       && r.EndDate >= bizDay
                                                       && r.IsValid);

            IList<RateCode> rates = new List<RateCode>();
            
            //房价的获取顺序 入住类型 -> 长包/团队 - 活动 - 个人会员 - 公司会员 

            if(!string.IsNullOrEmpty(param.CheckInType) && param.CheckInType!=EnumHelper.GetCode(CheckInType.NS))
            {
                var rate = sourceRates.FirstOrDefault(r => r.CodeValue==param.CheckInType);
                
                if(rate!=null)
                {
                    //如果入住类型不能叠加则直接返回
                    if (!rate.IsOverlay) return new List<RateCode>{rate};

                    rates.Add(rate);
                }
                
            }

            if(!string.IsNullOrEmpty(param.LongPacketNo))
            {
                var rate = sourceRates.FirstOrDefault(r => r.CodeValue == param.LongPacketNo);
                
                if (rate != null)
                {
                    rates.Add(rate);
                    
                    if (!rate.IsOverlay) return rates;
                }
            }

            if (!string.IsNullOrEmpty(param.ActiveCode))
            {
                var rate = sourceRates.FirstOrDefault(r => r.CodeValue == param.ActiveCode);

                if (rate != null)
                {
                    rates.Add(rate);

                    if (!rate.IsOverlay) return rates;
                }
            }

            if(!string.IsNullOrEmpty(param.IndividualMemberLevel))
            {
                var rate = sourceRates.FirstOrDefault(r => r.CodeValue == param.IndividualMemberLevel);

                if (rate != null)
                {
                    rates.Add(rate);

                    if (!rate.IsOverlay) return rates;
                }
            }

            if(!string.IsNullOrEmpty(param.CorporateMemberLevel))
            {
                var rate = sourceRates.FirstOrDefault(r => r.CodeValue == param.CorporateMemberLevel);

                if (rate != null)
                {
                    rates.Add(rate);

                    if (!rate.IsOverlay) return rates;
                }
            }
            
            return rates;
        }
        
        #endregion

    }
}
